﻿#include <iostream>
#include <list>
#include <windows.h>
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Choice.H>
#include <FL/Fl_Int_Input.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Check_Button.H>
#include <FL/Fl_PNG_Image.H>
#include "ini.h"
#include "resource.h"

#pragma execution_character_set("utf-8")
using namespace std;

struct Data
{
    Fl_Choice* did;
    Fl_Int_Input* ix;
    Fl_Int_Input* iy;
    Fl_Int_Input* iw;
    Fl_Int_Input* ih;
    Fl_Check_Button* cb;
};

struct Process
{
    char title[MAX_PATH];
    char exe[MAX_PATH];
    HWND hwnd;
};

list<struct Process> process;
CSimpleIniA ini;

BOOL CALLBACK enumWindowsProc(HWND hwnd, LPARAM lParam)
{
    if (hwnd == ::GetForegroundWindow()) {
        return TRUE;
    }
    if (!::IsWindowVisible(hwnd)) {
        return TRUE;
    }
    if (::GetParent(hwnd) != 0) {
        return TRUE;
    }
    struct Process p;
    p.hwnd = hwnd;
    WCHAR title[MAX_PATH] = { 0 };
    int len = ::GetWindowTextW(hwnd, title, MAX_PATH);
    if (!len) {
        return TRUE;
    }
    fl_utf8fromwc(p.title, MAX_PATH, title, len);
    Fl_Choice* wins = (Fl_Choice*)lParam;
    wins->add(p.title);

    DWORD did = 0;
    GetWindowThreadProcessId(hwnd, &did);
    HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, did);
    DWORD dlen = MAX_PATH;
    QueryFullProcessImageNameA(handle, 0, p.exe, &dlen);

    process.push_back(p);

    return TRUE;
}

void load_wins(Fl_Widget* w, void* data)
{
    Fl_Choice* wins = (Fl_Choice*)data;
    // 记录选择的 hwnd
    HWND hwnd = NULL;
    int index = wins->value();
    if (index > -1) {
        int i = 0;
        list<struct Process>::iterator it = process.begin();
        while (it != process.end())
        {
            if (index == i) {
                hwnd = it->hwnd;
                break;
            }
            ++it;
            ++i;
        }
    }
    wins->value(-1);
    // 刷新列表
    process.clear();
    wins->clear();
    setlocale(LC_ALL, "");
    ::EnumWindows(enumWindowsProc, (LPARAM)data);
    // 重选之前的
    if (hwnd != NULL) {
        list<struct Process>::iterator it = process.begin();
        int i = 0;
        while (it != process.end())
        {
            if (it->hwnd == hwnd) {
                wins->value(i);
                break;
            }
            ++it;
            ++i;
        }
        if (it == process.end()) {
            wins->value(0);
        }
    } else{
        wins->value(0);
    }
}

void set_no_title(HWND hwnd, int hide) {
    LONG st = ::GetWindowLongA(hwnd, GWL_STYLE);
    if (hide) {
        ::SetWindowLongA(hwnd, GWL_STYLE, st & ~(WS_CAPTION | WS_THICKFRAME | WS_SYSMENU));
    }
    else {
        ::SetWindowLongA(hwnd, GWL_STYLE, st | (WS_CAPTION | WS_THICKFRAME | WS_SYSMENU));
    }
}
/** 重置 */
void reset(Data* d) {

    d->ix->value("");
    d->iy->value("");
    d->iw->value("");
    d->ih->value("");
    d->cb->value(0);
}
// 窗口改变
void process_changed(Fl_Widget* wi, void* data) {
    auto d = static_cast<Data*>(data);
    auto index = d->did->value();
    list<struct Process>::iterator it = process.begin();
    int i = 0;
    char* exe = NULL;
    while (it != process.end())
    {
        if (index == i) {
            exe = it->exe;
            break;
        }
        ++it;
        ++i;
    }
    if (it == process.end()) {
        reset(d);
        return;
    }
    if (!ini.SectionExists(exe)) {
        reset(d);
        return;
    }
    d->ix->value(ini.GetValue(exe, "x"));
    d->iy->value(ini.GetValue(exe, "y"));
    d->iw->value(ini.GetValue(exe, "w"));
    d->ih->value(ini.GetValue(exe, "h"));
    d->cb->value(ini.GetLongValue(exe, "t"));
}
// 添加选项的回调函数
void set_win(Fl_Widget* wi, void* data)
{
    auto d = static_cast<Data*>(data);
    auto index = d->did->value();
    if (index < 0) {
        cout << "选择无效 " << index << endl;
        return;
    }
    list<struct Process>::iterator it = process.begin();
    int i = 0;
    char* exe = NULL;
    HWND hwnd = NULL;
    while (it != process.end())
    {
        if (index == i) {
            exe = it->exe;
            hwnd = it->hwnd;
            break;
        }
        ++it;
        ++i;
    }
    if (it == process.end() || hwnd == NULL) {
        cout << "无进程 " << index << " " << d->did->label() << endl;
        return;
    }
    int x = atoi(d->ix->value());
    int y = atoi(d->iy->value());
    int w = atoi(d->iw->value());
    if (w < 1) {
        return;
    }
    int h = atoi(d->ih->value());
    if (h < 1) {
        return;
    }
    set_no_title(hwnd, d->cb->value());
    ::SetWindowPos(
        hwnd, 0, x, y, w, h, SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOZORDER
    );
    // 保存
    ini.SetLongValue(exe, "x", x);
    ini.SetLongValue(exe, "y", y);
    ini.SetLongValue(exe, "w", w);
    ini.SetLongValue(exe, "h", h);
    ini.SetLongValue(exe, "t", d->cb->value());
    ini.SaveFile("config.ini");
}

static Fl_Font extra_font;
int main(int argc, char** argv)
{
    // 检查是否已有实例
    HANDLE hMutex = CreateMutex(NULL, FALSE, TEXT("Global\\WINPOS_MUTTEX"));
    DWORD dwError = GetLastError();

    if (ERROR_ALREADY_EXISTS == dwError || ERROR_ACCESS_DENIED == dwError) {
        return -1; // 已有实例运行
    }

    ini.SetUnicode();
    ini.LoadFile("config.ini");

    Fl_Window* window = new Fl_Window(320, 252, "窗口助手");
    auto res = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON1));
    window->icon(res);

    int y = 15;
    // 创建下拉框
    Fl_Choice* wins = new Fl_Choice(55, y, 208, 32, "窗口:");
    Fl_Button* refresh = new Fl_Button(268, y, 32, 30, "↻");
    refresh->callback(load_wins, wins);
    load_wins(refresh, wins);
    y += 38;

    // 输入框用于添加新选项
    Fl_Int_Input* ix = new Fl_Int_Input(55, y, 245, 32, "X:");
    y += 38;
    Fl_Int_Input* iy = new Fl_Int_Input(55, y, 245, 32, "Y:");
    y += 38;
    Fl_Int_Input* iw = new Fl_Int_Input(55, y, 245, 32, "宽度:");
    y += 38;
    Fl_Int_Input* ih = new Fl_Int_Input(55, y, 245, 32, "高度:");
    y += 38;

    Fl_Check_Button* cb = new Fl_Check_Button(55, y, 32, 32, "隐藏标题");

    // 添加按钮
    Fl_Button* btn = new Fl_Button(220, y, 80, 32, "确定");
    struct Data data { wins, ix, iy, iw, ih, cb };
    btn->callback(set_win, &data);
    wins->callback(process_changed, &data);

    window->end();
    window->show(argc, argv);
    return Fl::run();
}